import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:lib_network/interceptors/log_options.dart';

class HttpLogInterceptor extends Interceptor {
  HttpLogInterceptor({required this.logOptions, required this.logPrint});

  LogOptions logOptions;
  void Function(Object object) logPrint;

  /// Called when the request is about to be sent.
  @override
  void onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) {
    if (logOptions.has(LogOptions.formatRequestAscURL)) {
      logPrint(_cURLRepresentation(options));
    } else {
      List<String> logMessages = ['*** Request ***'];
      logMessages.add(_printKV('uri', options.uri));

      //options.headers;
      if (logOptions.has(LogOptions.requestHeaders)) {
        logMessages.add(_printKV('method', options.method));
        logMessages
            .add(_printKV('responseType', options.responseType.toString()));
        logMessages.add(_printKV('followRedirects', options.followRedirects));
        logMessages.add(
            _printKV('persistentConnection', options.persistentConnection));
        logMessages.add(_printKV('connectTimeout', options.connectTimeout));
        logMessages.add(_printKV('sendTimeout', options.sendTimeout));
        logMessages.add(_printKV('receiveTimeout', options.receiveTimeout));
        logMessages.add(_printKV(
            'receiveDataWhenStatusError', options.receiveDataWhenStatusError));
        logMessages.add(_printKV('extra', options.extra));

        logMessages.add("headers:");
        options.headers
            .forEach((key, v) => logMessages.add(_printKV(' $key', v)));
      }

      if (logOptions.has(LogOptions.requestBody)) {
        logMessages.add("data:");

        logMessages.addAll(_printAll(options.data));
      }

      logPrint(logMessages.join('\n'));
    }

    handler.next(options);
  }

  /// Called when the response is about to be resolved.
  @override
  void onResponse(
    Response response,
    ResponseInterceptorHandler handler,
  ) {
    logPrint(_printResponse(response).join('\n'));

    handler.next(response);
  }

  /// Called when an exception was occurred during the request.
  @override
  void onError(
    DioException err,
    ErrorInterceptorHandler handler,
  ) {
    if (logOptions.has(LogOptions.error)) {
      List<String> logMessages = ['*** DioException ***'];
      logMessages.add('uri: ${err.requestOptions.uri}');
      logMessages.add('$err');

      if (err.response != null) {
        logMessages.addAll(_printResponse(err.response!));
      }
    }

    handler.next(err);
  }

  String _cURLRepresentation(RequestOptions options) {
    List<String> components = ["\$ curl -i"];
    if (options.method.toUpperCase() == "GET") {
      components.add("-X ${options.method}");
    }

    if (options.headers.isNotEmpty) {
      options.headers.forEach((k, v) {
        if (k != "Cookie") {
          components.add("-H \"$k: $v\"");
        }
      });
    }

    if (options.contentType?.toUpperCase() ==
            ContentType.json.value.toUpperCase() &&
        options.data != null) {
      var data = json.encode(options.data);
      components.add("-d '$data'");
    }

    components.add("\"${options.uri.toString()}\"");

    return components.join('\\\n\t');
  }

  List<String> _printResponse(Response response) {
    List<String> logMessages = ['*** Response ***'];
    _printKV('uri', response.requestOptions.uri);
    logMessages.add(_printKV('uri', response.requestOptions.uri));

    if (logOptions.has(LogOptions.responseHeaders)) {
      logMessages.add(_printKV('statusCode', response.statusCode));

      if (response.isRedirect == true) {
        logMessages.add(_printKV('redirect', response.realUri));
      }

      logMessages.add("headers:");
      response.headers.forEach(
          (key, v) => logMessages.add(_printKV(' $key', v.join('\r\n\t'))));
    }
    if (logOptions.has(LogOptions.responseBody)) {
      logMessages.add('Response Text:');

      logMessages.addAll(_printAll(response.toString()));
    }

    return logMessages;
  }

  String _printKV(String key, Object? v) {
    return '$key: $v';
  }

  List<String> _printAll(msg) {
    try {
      return msg.toString().split('\n');
    } catch (e) {
      return [];
    }
  }
}
